/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package com.feilong.lib.javassist;

import com.feilong.lib.javassist.bytecode.BadBytecode;
import com.feilong.lib.javassist.bytecode.CodeAttribute;
import com.feilong.lib.javassist.bytecode.CodeIterator;
import com.feilong.lib.javassist.bytecode.ConstPool;
import com.feilong.lib.javassist.bytecode.MethodInfo;
import com.feilong.lib.javassist.convert.TransformAccessArrayField;
import com.feilong.lib.javassist.convert.TransformAfter;
import com.feilong.lib.javassist.convert.TransformBefore;
import com.feilong.lib.javassist.convert.TransformCall;
import com.feilong.lib.javassist.convert.TransformFieldAccess;
import com.feilong.lib.javassist.convert.TransformNew;
import com.feilong.lib.javassist.convert.TransformNewClass;
import com.feilong.lib.javassist.convert.TransformReadField;
import com.feilong.lib.javassist.convert.TransformWriteField;
import com.feilong.lib.javassist.convert.Transformer;

/**
 * Simple translator of method bodies
 * (also see the <code>javassist.expr</code> package).
 *
 * <p>
 * Instances of this class specifies how to instrument of the
 * bytecodes representing a method body. They are passed to
 * <code>CtClass.instrument()</code> or
 * <code>CtMethod.instrument()</code> as a parameter.
 *
 * <p>
 * Example:
 * 
 * <pre>
 * ClassPool cp = ClassPool.getDefault();
 * CtClass point = cp.get("Point");
 * CtClass singleton = cp.get("Singleton");
 * CtClass client = cp.get("Client");
 * CodeConverter conv = new CodeConverter();
 * conv.replaceNew(point, singleton, "makePoint");
 * client.instrument(conv);
 * </pre>
 *
 * <p>
 * This program substitutes "<code>Singleton.makePoint()</code>"
 * for all occurrences of "<code>new Point()</code>"
 * appearing in methods declared in a <code>Client</code> class.
 *
 * @see com.feilong.lib.javassist.CtClass#instrument(CodeConverter)
 * @see com.feilong.lib.javassist.CtMethod#instrument(CodeConverter)
 * @see com.feilong.lib.javassist.expr.ExprEditor
 */
public class CodeConverter{

    protected Transformer transformers = null;

    /**
     * Modify a method body so that instantiation of the specified class
     * is replaced with a call to the specified static method. For example,
     * <code>replaceNew(ctPoint, ctSingleton, "createPoint")</code>
     * (where <code>ctPoint</code> and <code>ctSingleton</code> are
     * compile-time classes for class <code>Point</code> and class
     * <code>Singleton</code>, respectively)
     * replaces all occurrences of:
     *
     * <pre>
     * new Point(x, y)
     * </pre>
     *
     * in the method body with:
     *
     * <pre>
     * Singleton.createPoint(x, y)
     * </pre>
     *
     * <p>
     * This enables to intercept instantiation of <code>Point</code>
     * and change the samentics. For example, the following
     * <code>createPoint()</code> implements the singleton pattern:
     *
     * <pre>
     * 
     * public static Point createPoint(int x,int y){
     *     if (aPoint == null)
     *         aPoint = new Point(x, y);
     *     return aPoint;
     * }
     * </pre>
     *
     * <p>
     * The static method call substituted for the original <code>new</code>
     * expression must be
     * able to receive the same set of parameters as the original
     * constructor. If there are multiple constructors with different
     * parameter types, then there must be multiple static methods
     * with the same name but different parameter types.
     *
     * <p>
     * The return type of the substituted static method must be
     * the exactly same as the type of the instantiated class specified by
     * <code>newClass</code>.
     *
     * @param newClass
     *            the instantiated class.
     * @param calledClass
     *            the class in which the static method is
     *            declared.
     * @param calledMethod
     *            the name of the static method.
     */
    public void replaceNew(CtClass newClass,CtClass calledClass,String calledMethod){
        transformers = new TransformNew(transformers, newClass.getName(), calledClass.getName(), calledMethod);
    }

    /**
     * Modify a method body so that instantiation of the class
     * specified by <code>oldClass</code>
     * is replaced with instantiation of another class <code>newClass</code>.
     * For example,
     * <code>replaceNew(ctPoint, ctPoint2)</code>
     * (where <code>ctPoint</code> and <code>ctPoint2</code> are
     * compile-time classes for class <code>Point</code> and class
     * <code>Point2</code>, respectively)
     * replaces all occurrences of:
     *
     * <pre>
     * new Point(x, y)
     * </pre>
     *
     * in the method body with:
     *
     * <pre>
     * new Point2(x, y)
     * </pre>
     *
     * <p>
     * Note that <code>Point2</code> must be type-compatible with <code>Point</code>.
     * It must have the same set of methods, fields, and constructors as the
     * replaced class.
     */
    public void replaceNew(CtClass oldClass,CtClass newClass){
        transformers = new TransformNewClass(transformers, oldClass.getName(), newClass.getName());
    }

    /**
     * Modify a method body so that field read/write expressions access
     * a different field from the original one.
     *
     * <p>
     * Note that this method changes only the filed name and the class
     * declaring the field; the type of the target object does not change.
     * Therefore, the substituted field must be declared in the same class
     * or a superclass of the original class.
     *
     * <p>
     * Also, <code>clazz</code> and <code>newClass</code> must specify
     * the class directly declaring the field. They must not specify
     * a subclass of that class.
     *
     * @param field
     *            the originally accessed field.
     * @param newClass
     *            the class declaring the substituted field.
     * @param newFieldname
     *            the name of the substituted field.
     */
    public void redirectFieldAccess(CtField field,CtClass newClass,String newFieldname){
        transformers = new TransformFieldAccess(transformers, field, newClass.getName(), newFieldname);
    }

    /**
     * Modify a method body so that an expression reading the specified
     * field is replaced with a call to the specified <i>static</i> method.
     * This static method receives the target object of the original
     * read expression as a parameter. It must return a value of
     * the same type as the field.
     *
     * <p>
     * For example, the program below
     *
     * <pre>
     * 
     * Point p = new Point();
     * 
     * int newX = p.x + 3;
     * </pre>
     *
     * <p>
     * can be translated into:
     *
     * <pre>
     * 
     * Point p = new Point();
     * 
     * int newX = Accessor.readX(p) + 3;
     * </pre>
     *
     * <p>
     * where
     *
     * <pre>
     * public class Accessor {
     *     public static int readX(Object target) { ... }
     * }
     * </pre>
     *
     * <p>
     * The type of the parameter of <code>readX()</code> must
     * be <code>java.lang.Object</code> independently of the actual
     * type of <code>target</code>. The return type must be the same
     * as the field type.
     *
     * @param field
     *            the field.
     * @param calledClass
     *            the class in which the static method is
     *            declared.
     * @param calledMethod
     *            the name of the static method.
     */
    public void replaceFieldRead(CtField field,CtClass calledClass,String calledMethod){
        transformers = new TransformReadField(transformers, field, calledClass.getName(), calledMethod);
    }

    /**
     * Modify a method body so that an expression writing the specified
     * field is replaced with a call to the specified static method.
     * This static method receives two parameters: the target object of
     * the original
     * write expression and the assigned value. The return type of the
     * static method is <code>void</code>.
     *
     * <p>
     * For example, the program below
     *
     * <pre>
     * Point p = new Point();
     * p.x = 3;
     * </pre>
     *
     * <p>
     * can be translated into:
     *
     * <pre>
     * Point p = new Point();
     * Accessor.writeX(3);
     * </pre>
     *
     * <p>
     * where
     *
     * <pre>
     * public class Accessor {
     *     public static void writeX(Object target, int value) { ... }
     * }
     * </pre>
     *
     * <p>
     * The type of the first parameter of <code>writeX()</code> must
     * be <code>java.lang.Object</code> independently of the actual
     * type of <code>target</code>. The type of the second parameter
     * is the same as the field type.
     *
     * @param field
     *            the field.
     * @param calledClass
     *            the class in which the static method is
     *            declared.
     * @param calledMethod
     *            the name of the static method.
     */
    public void replaceFieldWrite(CtField field,CtClass calledClass,String calledMethod){
        transformers = new TransformWriteField(transformers, field, calledClass.getName(), calledMethod);
    }

    /**
     * Modify a method body, so that ALL accesses to an array are replaced with
     * calls to static methods within another class. In the case of reading an
     * element from the array, this is replaced with a call to a static method with
     * the array and the index as arguments, the return value is the value read from
     * the array. If writing to an array, this is replaced with a call to a static
     * method with the array, index and new value as parameters, the return value of
     * the static method is <code>void</code>.
     * 
     * <p>
     * The <code>calledClass</code> parameter is the class containing the static methods to be used
     * for array replacement. The <code>names</code> parameter points to an implementation of
     * <code>ArrayAccessReplacementMethodNames</code> which specifies the names of the method to be
     * used for access for each type of array. For example reading from an <code>int[]</code> will
     * require a different method than if writing to an <code>int[]</code>, and writing to a <code>long[]</code>
     * will require a different method than if writing to a <code>byte[]</code>. If the implementation
     * of <code>ArrayAccessReplacementMethodNames</code> does not contain the name for access for a
     * type of array, that access is not replaced.
     * 
     * <p>
     * A default implementation of <code>ArrayAccessReplacementMethodNames</code> called
     * <code>DefaultArrayAccessReplacementMethodNames</code> has been provided and is what is used in the
     * following example. This also assumes that <code>'foo.ArrayAdvisor'</code> is the name of the
     * <code>CtClass</code> passed in.
     * 
     * <p>
     * If we have the following class:
     * 
     * <pre>
     * class POJO{
     * 
     *     int[] ints = new int[] { 1, 2, 3, 4, 5 };
     * 
     *     long[] longs = new int[] { 10, 20, 30 };
     * 
     *     Object objects = new Object[] { true, false };
     * 
     *     Integer[] integers = new Integer[] { new Integer(10) };
     * }
     * </pre>
     * 
     * and this is accessed as:
     * 
     * <pre>
     * POJO p = new POJO();
     * 
     * //Write to int array
     * p.ints[2] = 7;
     * 
     * //Read from int array
     * int i = p.ints[2];
     * 
     * //Write to long array
     * p.longs[2] = 1000L;
     * 
     * //Read from long array
     * long l = p.longs[2];
     * 
     * //Write to Object array
     * p.objects[2] = "Hello";
     * 
     * //Read from Object array
     * Object o = p.objects[2];
     * 
     * //Write to Integer array
     * Integer integer = new Integer(5);
     * p.integers[0] = integer;
     * 
     * //Read from Object array
     * integer = p.integers[0];
     * </pre>
     * 
     * Following instrumentation we will have
     * 
     * <pre>
     * POJO p = new POJO();
     * 
     * //Write to int array
     * ArrayAdvisor.arrayWriteInt(p.ints, 2, 7);
     * 
     * //Read from int array
     * int i = ArrayAdvisor.arrayReadInt(p.ints, 2);
     * 
     * //Write to long array
     * ArrayAdvisor.arrayWriteLong(p.longs, 2, 1000L);
     * 
     * //Read from long array
     * long l = ArrayAdvisor.arrayReadLong(p.longs, 2);
     * 
     * //Write to Object array
     * ArrayAdvisor.arrayWriteObject(p.objects, 2, "Hello");
     * 
     * //Read from Object array
     * Object o = ArrayAdvisor.arrayReadObject(p.objects, 2);
     * 
     * //Write to Integer array
     * Integer integer = new Integer(5);
     * ArrayAdvisor.arrayWriteObject(p.integers, 0, integer);
     * 
     * //Read from Object array
     * integer = ArrayAdvisor.arrayWriteObject(p.integers, 0);
     * </pre>
     * 
     * @see DefaultArrayAccessReplacementMethodNames
     * 
     * @param calledClass
     *            the class containing the static methods.
     * @param names
     *            contains the names of the methods to replace
     *            the different kinds of array access with.
     */
    public void replaceArrayAccess(CtClass calledClass,ArrayAccessReplacementMethodNames names) throws NotFoundException{
        transformers = new TransformAccessArrayField(transformers, calledClass.getName(), names);
    }

    /**
     * Modify method invocations in a method body so that a different
     * method will be invoked.
     *
     * <p>
     * Note that the target object, the parameters, or
     * the type of invocation
     * (static method call, interface call, or private method call)
     * are not modified. Only the method name is changed. The substituted
     * method must have the same signature that the original one has.
     * If the original method is a static method, the substituted method
     * must be static.
     *
     * @param origMethod
     *            original method
     * @param substMethod
     *            substituted method
     */
    public void redirectMethodCall(CtMethod origMethod,CtMethod substMethod) throws CannotCompileException{
        String d1 = origMethod.getMethodInfo2().getDescriptor();
        String d2 = substMethod.getMethodInfo2().getDescriptor();
        if (!d1.equals(d2))
            throw new CannotCompileException("signature mismatch: " + substMethod.getLongName());

        int mod1 = origMethod.getModifiers();
        int mod2 = substMethod.getModifiers();
        if (Modifier.isStatic(mod1) != Modifier.isStatic(mod2) || (Modifier.isPrivate(mod1) && !Modifier.isPrivate(mod2))
                        || origMethod.getDeclaringClass().isInterface() != substMethod.getDeclaringClass().isInterface())
            throw new CannotCompileException("invoke-type mismatch " + substMethod.getLongName());

        transformers = new TransformCall(transformers, origMethod, substMethod);
    }

    /**
     * Correct invocations to a method that has been renamed.
     * If a method is renamed, calls to that method must be also
     * modified so that the method with the new name will be called.
     *
     * <p>
     * The method must be declared in the same class before and
     * after it is renamed.
     *
     * <p>
     * Note that the target object, the parameters, or
     * the type of invocation
     * (static method call, interface call, or private method call)
     * are not modified. Only the method name is changed.
     *
     * @param oldMethodName
     *            the old name of the method.
     * @param newMethod
     *            the method with the new name.
     * @see com.feilong.lib.javassist.CtMethod#setName(String)
     */
    public void redirectMethodCall(String oldMethodName,CtMethod newMethod) throws CannotCompileException{
        transformers = new TransformCall(transformers, oldMethodName, newMethod);
    }

    /**
     * Insert a call to another method before an existing method call.
     * That "before" method must be static. The return type must be
     * <code>void</code>. As parameters, the before method receives
     * the target object and all the parameters to the originally invoked
     * method. For example, if the originally invoked method is
     * <code>move()</code>:
     *
     * <pre>
     * class Point {
     *     Point move(int x, int y) { ... }
     * }
     * </pre>
     *
     * <p>
     * Then the before method must be something like this:
     *
     * <pre>
     * class Verbose {
     *     static void print(Point target, int x, int y) { ... }
     * }
     * </pre>
     *
     * <p>
     * The <code>CodeConverter</code> would translate bytecode
     * equivalent to:
     *
     * <pre>
     * 
     * Point p2 = p.move(x + y, 0);
     * </pre>
     *
     * <p>
     * into the bytecode equivalent to:
     *
     * <pre>
     * int tmp1 = x + y;
     * int tmp2 = 0;
     * Verbose.print(p, tmp1, tmp2);
     * Point p2 = p.move(tmp1, tmp2);
     * </pre>
     *
     * @param origMethod
     *            the method originally invoked.
     * @param beforeMethod
     *            the method invoked before
     *            <code>origMethod</code>.
     */
    public void insertBeforeMethod(CtMethod origMethod,CtMethod beforeMethod) throws CannotCompileException{
        try{
            transformers = new TransformBefore(transformers, origMethod, beforeMethod);
        }catch (NotFoundException e){
            throw new CannotCompileException(e);
        }
    }

    /**
     * Inserts a call to another method after an existing method call.
     * That "after" method must be static. The return type must be
     * <code>void</code>. As parameters, the after method receives
     * the target object and all the parameters to the originally invoked
     * method. For example, if the originally invoked method is
     * <code>move()</code>:
     *
     * <pre>
     * class Point {
     *     Point move(int x, int y) { ... }
     * }
     * </pre>
     *
     * <p>
     * Then the after method must be something like this:
     *
     * <pre>
     * class Verbose {
     *     static void print(Point target, int x, int y) { ... }
     * }
     * </pre>
     *
     * <p>
     * The <code>CodeConverter</code> would translate bytecode
     * equivalent to:
     *
     * <pre>
     * 
     * Point p2 = p.move(x + y, 0);
     * </pre>
     *
     * <p>
     * into the bytecode equivalent to:
     *
     * <pre>
     * int tmp1 = x + y;
     * int tmp2 = 0;
     * Point p2 = p.move(tmp1, tmp2);
     * Verbose.print(p, tmp1, tmp2);
     * </pre>
     *
     * @param origMethod
     *            the method originally invoked.
     * @param afterMethod
     *            the method invoked after
     *            <code>origMethod</code>.
     */
    public void insertAfterMethod(CtMethod origMethod,CtMethod afterMethod) throws CannotCompileException{
        try{
            transformers = new TransformAfter(transformers, origMethod, afterMethod);
        }catch (NotFoundException e){
            throw new CannotCompileException(e);
        }
    }

    /**
     * Performs code conversion.
     */
    protected void doit(CtClass clazz,MethodInfo minfo,ConstPool cp) throws CannotCompileException{
        Transformer t;
        CodeAttribute codeAttr = minfo.getCodeAttribute();
        if (codeAttr == null || transformers == null)
            return;
        for (t = transformers; t != null; t = t.getNext())
            t.initialize(cp, clazz, minfo);

        CodeIterator iterator = codeAttr.iterator();
        while (iterator.hasNext()){
            try{
                int pos = iterator.next();
                for (t = transformers; t != null; t = t.getNext())
                    pos = t.transform(clazz, pos, iterator, cp);
            }catch (BadBytecode e){
                throw new CannotCompileException(e);
            }
        }

        int locals = 0;
        int stack = 0;
        for (t = transformers; t != null; t = t.getNext()){
            int s = t.extraLocals();
            if (s > locals)
                locals = s;

            s = t.extraStack();
            if (s > stack)
                stack = s;
        }

        for (t = transformers; t != null; t = t.getNext())
            t.clean();

        if (locals > 0)
            codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals);

        if (stack > 0)
            codeAttr.setMaxStack(codeAttr.getMaxStack() + stack);

        try{
            minfo.rebuildStackMapIf6(clazz.getClassPool(), clazz.getClassFile2());
        }catch (BadBytecode b){
            throw new CannotCompileException(b.getMessage(), b);
        }
    }

    /**
     * Interface containing the method names to be used
     * as array access replacements.
     *
     * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
     * @version $Revision: 1.16 $
     */
    public interface ArrayAccessReplacementMethodNames{

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;I)B</code> to replace reading from a byte[].
         */
        String byteOrBooleanRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;IB)V</code> to replace writing to a byte[].
         */
        String byteOrBooleanWrite();

        /**
         * @return the name of a static method with the signature
         *         <code>(Ljava/lang/Object;I)C</code> to replace reading from a char[].
         */
        String charRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;IC)V</code> to replace writing to a byte[].
         */
        String charWrite();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;I)D</code> to replace reading from a double[].
         */
        String doubleRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;ID)V</code> to replace writing to a double[].
         */
        String doubleWrite();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;I)F</code> to replace reading from a float[].
         */
        String floatRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;IF)V</code> to replace writing to a float[].
         */
        String floatWrite();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;I)I</code> to replace reading from a int[].
         */
        String intRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;II)V</code> to replace writing to a int[].
         */
        String intWrite();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;I)J</code> to replace reading from a long[].
         */
        String longRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;IJ)V</code> to replace writing to a long[].
         */
        String longWrite();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;I)Ljava/lang/Object;</code>
         * to replace reading from a Object[] (or any subclass of object).
         */
        String objectRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;ILjava/lang/Object;)V</code>
         * to replace writing to a Object[] (or any subclass of object).
         */
        String objectWrite();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;I)S</code> to replace reading from a short[].
         */
        String shortRead();

        /**
         * Returns the name of a static method with the signature
         * <code>(Ljava/lang/Object;IS)V</code> to replace writing to a short[].
         */
        String shortWrite();
    }

    /**
     * Default implementation of the <code>ArrayAccessReplacementMethodNames</code>
     * interface giving default values for method names to be used for replacing
     * accesses to array elements.
     *
     * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
     * @version $Revision: 1.16 $
     */
    public static class DefaultArrayAccessReplacementMethodNames implements ArrayAccessReplacementMethodNames{

        /**
         * Returns "arrayReadByteOrBoolean" as the name of the static method with the signature
         * (Ljava/lang/Object;I)B to replace reading from a byte[].
         */
        @Override
        public String byteOrBooleanRead(){
            return "arrayReadByteOrBoolean";
        }

        /**
         * Returns "arrayWriteByteOrBoolean" as the name of the static method with the signature
         * (Ljava/lang/Object;IB)V to replace writing to a byte[].
         */
        @Override
        public String byteOrBooleanWrite(){
            return "arrayWriteByteOrBoolean";
        }

        /**
         * Returns "arrayReadChar" as the name of the static method with the signature
         * (Ljava/lang/Object;I)C to replace reading from a char[].
         */
        @Override
        public String charRead(){
            return "arrayReadChar";
        }

        /**
         * Returns "arrayWriteChar" as the name of the static method with the signature
         * (Ljava/lang/Object;IC)V to replace writing to a byte[].
         */
        @Override
        public String charWrite(){
            return "arrayWriteChar";
        }

        /**
         * Returns "arrayReadDouble" as the name of the static method with the signature
         * (Ljava/lang/Object;I)D to replace reading from a double[].
         */
        @Override
        public String doubleRead(){
            return "arrayReadDouble";
        }

        /**
         * Returns "arrayWriteDouble" as the name of the static method with the signature
         * (Ljava/lang/Object;ID)V to replace writing to a double[].
         */
        @Override
        public String doubleWrite(){
            return "arrayWriteDouble";
        }

        /**
         * Returns "arrayReadFloat" as the name of the static method with the signature
         * (Ljava/lang/Object;I)F to replace reading from a float[].
         */
        @Override
        public String floatRead(){
            return "arrayReadFloat";
        }

        /**
         * Returns "arrayWriteFloat" as the name of the static method with the signature
         * (Ljava/lang/Object;IF)V to replace writing to a float[].
         */
        @Override
        public String floatWrite(){
            return "arrayWriteFloat";
        }

        /**
         * Returns "arrayReadInt" as the name of the static method with the signature
         * (Ljava/lang/Object;I)I to replace reading from a int[].
         */
        @Override
        public String intRead(){
            return "arrayReadInt";
        }

        /**
         * Returns "arrayWriteInt" as the name of the static method with the signature
         * (Ljava/lang/Object;II)V to replace writing to a int[].
         */
        @Override
        public String intWrite(){
            return "arrayWriteInt";
        }

        /**
         * Returns "arrayReadLong" as the name of the static method with the signature
         * (Ljava/lang/Object;I)J to replace reading from a long[].
         */
        @Override
        public String longRead(){
            return "arrayReadLong";
        }

        /**
         * Returns "arrayWriteLong" as the name of the static method with the signature
         * (Ljava/lang/Object;IJ)V to replace writing to a long[].
         */
        @Override
        public String longWrite(){
            return "arrayWriteLong";
        }

        /**
         * Returns "arrayReadObject" as the name of the static method with the signature
         * (Ljava/lang/Object;I)Ljava/lang/Object; to replace reading from a Object[] (or any subclass of object).
         */
        @Override
        public String objectRead(){
            return "arrayReadObject";
        }

        /**
         * Returns "arrayWriteObject" as the name of the static method with the signature
         * (Ljava/lang/Object;ILjava/lang/Object;)V to replace writing to a Object[] (or any subclass of object).
         */
        @Override
        public String objectWrite(){
            return "arrayWriteObject";
        }

        /**
         * Returns "arrayReadShort" as the name of the static method with the signature
         * (Ljava/lang/Object;I)S to replace reading from a short[].
         */
        @Override
        public String shortRead(){
            return "arrayReadShort";
        }

        /**
         * Returns "arrayWriteShort" as the name of the static method with the signature
         * (Ljava/lang/Object;IS)V to replace writing to a short[].
         */
        @Override
        public String shortWrite(){
            return "arrayWriteShort";
        }
    }
}
